from typing import Optional
from pydantic import BaseModel
import pandas as pd

class Kline(BaseModel):
    Open: Optional[str] = ''
    Close: Optional[str] = ''
    High: Optional[str] = ''
    Low: Optional[str] = ''
    Volume: Optional[str] = ''
    OpenTime: Optional[str] = ''
    CloseTime: Optional[str] = ''
    TakerBuyBaseVolume: Optional[str] = ''

class Indicator:
    def __init__(self, kline: list[Kline]):
       self.kline = kline or []
       self.closes = [float(item['Close']) for item in self.kline]
       self.volume = [float(item['Volume']) for item in self.kline]
       self.TakerBuyBaseVolume = [float(item['TakerBuyBaseVolume']) for item in self.kline]

    def MA_line(self, period=5, type='Close'):
        if type == 'Volume':
            if len(self.volume) < period:
                return None
            return sum(self.volume[-period:]) / period
        else:
            if len(self.closes) < period:
                return None
            return sum(self.closes[-period:]) / period

    def RSI_line(self, period=6):
        if len(self.closes) < period + 1:
            return None
        delta = pd.Series(self.closes).diff()
        gain = delta.where(delta > 0, 0.0)
        loss = -delta.where(delta < 0, 0.0)

        avg_gain = gain.rolling(window=period).mean()
        avg_loss = loss.rolling(window=period).mean()

        rs = avg_gain / avg_loss
        rsi = 100 - (100 / (1 + rs))
        return rsi

    def cross_line(self, short_period=7, long_period=25):
        if len(self.closes) < long_period + 1:
            return None  # 数据不足
        def ma_line(closes, period):
            if len(closes) < period:
                return None
            return sum(closes[-period:]) / period
        # 当前和前一周期的短期MA
        short_ma_prev = ma_line(self.closes[:-1], short_period)
        short_ma_now = ma_line(self.closes, short_period)

        # 当前和前一周期的长期MA
        long_ma_prev = ma_line(self.closes[:-1], long_period)
        long_ma_now = ma_line(self.closes, long_period)

        # 判断金叉和死叉
        if short_ma_prev < long_ma_prev and short_ma_now > long_ma_now:
            return "golden_cross"  # 金叉
        elif short_ma_prev > long_ma_prev and short_ma_now < long_ma_now:
            return "death_cross"   # 死叉
        else:
            return "no_signal"

import requests
import json
from fastapi import APIRouter, File
from fastapi.responses import JSONResponse
from pathlib import Path
from datetime import datetime

router = APIRouter(default_response_class=JSONResponse)

# 定义图片存储路径
UPLOAD_DIR = Path("static/log")
# 创建目录（如果不存在）
UPLOAD_DIR.mkdir(parents=True, exist_ok=True)

def set_log(message: str, fileText: str = ''):
    timer = datetime.now().strftime("%Y-%m-%d")
    file_path = f'static/log/{fileText}_{timer}.txt'
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    log_entry = f"[{timestamp}]：\n{message}\n\n"
    with open(file_path, 'a', encoding='utf-8') as f:
        f.write(log_entry)

positions = []
profit = 0

def set_order_df(current, klineLast):
    global positions
    global profit
    exists = next((item for item in positions if item['symbol'] == 'ETHUSDT'), None)
    klineLast = current[-1]
    mas = Indicator(current)
    price = float(klineLast['Close'])
    indicator = {
        'RSI6': mas.RSI_line(6).iloc[-1],
        'ma7': float(mas.MA_line(7)),
        'ma25': float(mas.MA_line(25)),
    }
    # 判断是否存在仓位，存在仓位不再买入
    if exists and abs(float(exists['positionAmt'])) > 0:
        entry_price = float(exists['entryPrice'])
        stop_price = False
        stop_ma = False
        # 判断当前是否已经到达止盈止损位置，进行平仓
        if exists['positionSide'] == 'LONG':
            stop_price = (price - entry_price) / entry_price if (price < entry_price and (entry_price - price) / entry_price > 0.005) else stop_price
            stop_price = (price - entry_price) / entry_price if (price > entry_price and (price - entry_price) / entry_price > 0.015) else stop_price
            stop_ma = price < indicator['ma25']
            if stop_ma:
               stop_price = (indicator['ma25'] - entry_price) / entry_price 
        elif exists['positionSide'] == 'SHORT':
            stop_price = (entry_price - price) / entry_price if (price > entry_price and (price - entry_price) / entry_price > 0.005) else stop_price
            stop_price = (entry_price - price) / entry_price if (price < entry_price and (entry_price - price) / entry_price > 0.015) else stop_price
            stop_ma = price > indicator['ma25']
            if stop_ma:
               stop_price = (entry_price - indicator['ma25']) / entry_price 
        # 止损/止盈
        if stop_price:
            set_log(json.dumps({
                'positionSide': exists['positionSide'],
                'side': 'SELL' if exists['positionSide'] == 'LONG' else 'BUY',
                'type': 'MARKET',
                'quantity': exists['positionAmt'],
                'symbol': exists['symbol'],
                'stopPrice': price,
                'profit': stop_price,
                'kline': klineLast,
                'indicator': indicator
            }), 'stop')
            positions = []
            profit = profit + stop_price
            set_log(f'单次下单营收：{stop_price}，总营收：{profit}', 'profit')
        return
    else:
        cross = mas.cross_line()
        if cross == 'golden_cross':
            # 金叉上涨
            set_log(json.dumps({
                'positionSide': 'LONG',
                'side': 'BUY',
                'type': 'MARKET',
                'symbol': 'ETHUSDT',
                'quantity': round(float(50) / float(klineLast['Close']) * float(50), 4),
                'price': (price + indicator['ma7']) / 2,
                'stopPrice': float(klineLast['Close']) - float(klineLast['Close']) * 0.005,
                'klineLast': klineLast,
                'indicator': indicator
            }), 'backtest')
            positions = [{
                'positionSide': 'LONG',
                'side': 'BUY',
                'type': 'MARKET',
                'symbol': 'ETHUSDT',
                'positionAmt': round(float(50) / float(klineLast['Close']) * float(50), 4),
                'entryPrice': (price + indicator['ma7']) / 2,
                'stopPrice': float(klineLast['Close']) - float(klineLast['Close']) * 0.005,
            }]
        elif cross == 'death_cross':
            # 死叉下跌
            set_log(json.dumps({
                'positionSide': 'SHORT',
                'side': 'SELL',
                'type': 'MARKET',
                'symbol': 'BTCUSDT',
                'quantity': round(float(50) / float(klineLast['Close']) * float(50), 4),
                'price': (price + indicator['ma7']) / 2,
                'stopPrice': float(klineLast['Close']) + float(klineLast['Close']) * 0.005,
                'klineLast': klineLast,
                'indicator': indicator
            }), 'backtest')
            positions = [{
                'positionSide': 'LONG',
                'side': 'BUY',
                'type': 'MARKET',
                'symbol': 'ETHUSDT',
                'positionAmt': round(float(50) / float(klineLast['Close']) * float(50), 4),
                'entryPrice': (price + indicator['ma7']) / 2,
                'stopPrice': float(klineLast['Close']) - float(klineLast['Close']) * 0.005,
            }]

def binance_klines():
    params = {
        'symbol': 'ETHUSDT',
        'interval': '1h',
        'limit':300
    }

    response = requests.get("https://fapi.binance.com/fapi/v1/klines", params=params)
    data = response.json()

    # 将数据转换为DataFrame
    df = pd.DataFrame(data, columns=[
        "OpenTime", "Open", "High", "Low", "Close", "Volume",
        "CloseTime", "QuoteVolume", "Trades", "TakerBuyBaseVolume",
        "TakerBuyQuoteVolume", "Ignore"
    ])
    df = df.drop(columns=["Ignore", "Trades", "QuoteVolume", "TakerBuyQuoteVolume"])
    # 转换时间戳为日期时间格式
    df['OpenTime'] = pd.to_datetime(df['OpenTime'], unit='ms').dt.tz_localize("UTC").dt.tz_convert("Asia/Shanghai").dt.strftime('%Y-%m-%d %H:%M')
    df['CloseTime'] = pd.to_datetime(df['CloseTime'], unit='ms').dt.tz_localize("UTC").dt.tz_convert("Asia/Shanghai").dt.strftime('%Y-%m-%d %H:%M')

    # 将DataFrame转换为JSON格式
    result = df.to_dict(orient='records')

    # 回测
    for i in range(30, len(result) + 1):
        current = result[:i]
        klineLast = current[-1]
        set_order_df(current, klineLast)

# binance_klines()

filters = [
    {
        "filterType": "PRICE_FILTER",
        "tickSize": "0.10",
        "minPrice": "556.80",
        "maxPrice": "4529764"
    },
    {
        "filterType": "LOT_SIZE",
        "stepSize": "0.001",
        "minQty": "0.001",
        "maxQty": "1000"
    },
    # 其他过滤器...
]

def get_tick_and_step(filters: list[dict]) -> tuple[float, float]:
    tick_size = None
    step_size = None
    for f in filters:
        if f.get("filterType") == "PRICE_FILTER":
            tick_size = float(f["tickSize"])
        elif f.get("filterType") == "LOT_SIZE":
            step_size = float(f["stepSize"])
    def get_precision_from_step(step_str: str) -> int:
        """根据 tickSize 或 stepSize 字符串返回允许的小数位数"""
        if '.' not in step_str:
            return 0
        return len(step_str.rstrip('0').split('.')[1])
    tick_size = get_precision_from_step(str(tick_size))
    step_size = get_precision_from_step(str(step_size))
    return tick_size, step_size

tick, step = get_tick_and_step(filters)
print("tickSize =", tick)   # 输出: tickSize = 0.1
print("stepSize =", step)   # 输出: stepSize = 0.001
